/*
 *  jOTR - Java Off-The-Record Messaging Library
 *  Copyright (C) 2007-2008 Markus Karnik
 *
 *  This program is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 3 of the License, 
 *  or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along 
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
 *  
 */
package de.karnik.libjotr;

import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;

import de.karnik.helper.LogFunctions;

/**
 * The storage for the otr session data and status of a contact. 
 * 
 * @author Markus Karnik - markus.karnik@my-design.org
 * @version 1.0
 * @since 1.0
 */
public class JotrContext implements JotrConstants {
	
	// #############################################################################
	// Key configuration values
	// #############################################################################	
	/**
	 * The minumum dh exponent key length in bit.
	 */
	public static final int MIN_DH_KEY_LENGTH = 320;
	/**
	 * The aes key length in bin.
	 */
	public static final int BIT_COUNT_AES_KEY = 128;
	/**
	 * The aes key length in byte.
	 */
	public static final int BYTE_COUNT_AES_KEY = BIT_COUNT_AES_KEY / 8;
	
	// #############################################################################
	// Message States
	// #############################################################################
	
	/**
	 * This state indicates that outgoing messages are sent without encryption.
	 * This is the state that is used before an OTR conversation is initiated. 
	 * This is the initial state, and the only way to subsequently enter this 
	 * state is for the user to explicitly request to do so via some UI operation.
	 */
	public static final int MSGSTATE_PLAINTEXT = 0;
	/**
	 * This state indicates that outgoing messages are sent encrypted. 
	 * This is the state that is used during an OTR conversation. The only 
	 * way to enter this state is for the authentication state machine (below) 
	 * to successfully complete.
	 */
	public static final int MSGSTATE_ENCRYPTED = 1;
    /**
     * This state indicates that outgoing messages are not delivered at all. 
     * This state is entered only when the other party indicates he has terminated 
     * his side of the OTR conversation. For example, if Alice and Bob are having 
     * an OTR conversation, and Bob instructs his OTR client to end its private 
     * session with Alice (for example, by logging out), Alice will be notified of 
     * this, and her client will switch to MSGSTATE_FINISHED mode. This prevents 
     * Alice from accidentally sending a message to Bob in plaintext. (Consider what 
     * happens if Alice was in the middle of typing a private message to Bob when he 
     * suddenly logs out, just as Alice hits Enter.)
     */
    public static final int MSGSTATE_FINISHED = 2;
     
    /**
     * The message state variable, msgstate, controls what happens to outgoing
     * messages typed by the user. It can take one of three values.
     * 
     * @see #MSGSTATE_PLAINTEXT
     * @see #MSGSTATE_ENCRYPTED
     * @see #MSGSTATE_FINISHED
     */
    private int messageState = MSGSTATE_PLAINTEXT;
    
	// #############################################################################
	// Authentication state
	// #############################################################################
    /**
     * This state indicates that the authentication protocol is not currently in progress. 
     * This is the initial state.
     */
    public static final int AUTHSTATE_NONE = 0;
    /**
     * After Bob initiates the authentication protocol by sending Alice the D-H Commit Message,
     * he enters this state to await Alice's reply.
     */
    public static final int AUTHSTATE_AWAITING_DHKEY = 1;
    /**
     * After Alice receives Bob's D-H Commit Message, and replies with her own D-H Key Message, 
     * she enters this state to await Bob's reply. 
     */
    public static final int  AUTHSTATE_AWAITING_REVEALSIG = 2;
    /**
     * After Bob receives Alice's D-H Key Message, and replies with his own Reveal Signature Message, 
     * he enters this state to await Alice's reply.
     */
    public static final int AUTHSTATE_AWAITING_SIG = 4;
    /**
     * For OTR version 1 compatibility, if Bob sends a version 1 Key Exchange Message to Alice, 
     * he enters this state to await Alice's reply.
     */
    public static final int  AUTHSTATE_V1_SETUP = 8;    
    /**
     * The authentication state variable, authstate, can take one of four values 
     * (plus one extra for OTR version 1 compatibility).
     * After:<br><br>
     *
	 *    * Alice (in AUTHSTATE_AWAITING_REVEALSIG) receives Bob's Reveal Signature <br>
     *      Message (and replies with her own Signature Message),<br>
	 *    * Alice (in AUTHSTATE_NONE) receives Bob's Version 1 Key Exchange Message <br>
     *      (and replies with her own Key Exchange Message),<br>
	 *    * Bob (in AUTHSTATE_AWAITING_SIG) receives Alice's Signature Message, or<br>
	 *    * Bob (in AUTHSTATE_V1_SETUP) receives Alice's Version 1 Key Exchange<br> 
     *      Message,<br><br>
     *
	 * then, assuming the signature verifications succeed, the msgstate variable is <br>
     * transitioned to MSGSTATE_ENCRYPTED. Regardless of whether the signature <br>
     * verifications succeed, the authstate variable is transitioned to <br>
     * AUTHSTATE_NONE.<br>
     *
     * @see #AUTHSTATE_NONE
     * @see #AUTHSTATE_AWAITING_DHKEY
     * @see #AUTHSTATE_AWAITING_REVEALSIG
     * @see #AUTHSTATE_AWAITING_SIG
     * @see #AUTHSTATE_V1_SETUP
     */
    private int authstate = AUTHSTATE_NONE;
    	    
	/**
	 * Allow version 1 of the OTR protocol to be used.
	 */
	private boolean allowV1 = true;
	/**
	 * Allow version 2 of the OTR protocol to be used.
	 */
	private boolean allowV2 = true;
    /**
     * Refuse to send unencrypted messages. 
     */
    private boolean requireEncryption = false;
    /**
     * Advertise your support of OTR using the whitespace tag. 
     */
    private boolean sendWhitespaceTag = true;
    /**
     * Start the OTR AKE when you receive a whitespace tag.
     */
    private boolean whitespaceStartAke = true;
    /**
     * Start the OTR AKE when you receive an OTR Error Message. 
     */
    private boolean errorStartAke = false;
    
    /**
     * The Diffie-Hellman computation object.
     */
    private JotrDH jotrDh = null;
    /**
     * The dsa key storage object.
     */
    private JotrDsaKeyStorage dsaKeyStorage = null;
    /**
     * The temporary aes key of the contact. Only used in the ake phase.
     */
    private byte[] aesKey = null;
    /**
     * The aes encrypted public key of the contact. Only used in the ake phase.
     */
    private byte[] remoteAesEncryptedPublicKey = null;
    
    /**
     * The remote public key hash value.
     */
    private byte[] remotePublicKeyHash = null;
    /**
     * The local public key hash value.
     */
    private byte[] localePublicKeyHash = null;
    
    /**
     * The used otr version.
     */
    private int version = 0;
    /**
     * The local aes ctr counter.
     */
    private long ourAesCounter = 0;
    /**
     * The remote aes ctr counter.
     */
    private long theirAesCounter = 0;
	
	/**
	 * Constructor for a new JotrContext object.<br><br>
	 * Creates a new JotrContext object with the given initial values.<br><br>
	 * For further information see OTR documentation.
	 * 
	 * @param allowV1 Indicator for version 1 compatibility. (Not yet supported)
	 * @param allowV2 Indicator for version 2 compatibility. (Standard)
	 * @param requireEncryption Indicator for required encryption. 
	 * @param sendWhitespaceTag Indicator for sending whitespace tags.
	 * @param whitespaceStartAke Indicator for automatically starting ake when receiving valid whitespace tags. 
	 * @param errorStartAke Indicator for automatically starting ake when receiving an error. Renew the auth. 
	 * @throws InvalidAlgorithmParameterException
	 * @throws NoSuchAlgorithmException
	 */
	public JotrContext( boolean allowV1, boolean allowV2,
						boolean requireEncryption, boolean sendWhitespaceTag,
						boolean whitespaceStartAke, boolean errorStartAke ) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
		this();
		this.allowV1 = allowV1;
		this.allowV2 = allowV2;
		this.requireEncryption = requireEncryption;
		this.sendWhitespaceTag = sendWhitespaceTag;
		this.whitespaceStartAke = whitespaceStartAke;
		this.errorStartAke = errorStartAke;
	}

	/**
	 * Constructor for a new JotrContext object with standard values.
	 * 
	 * @throws InvalidAlgorithmParameterException
	 * @throws NoSuchAlgorithmException
	 */
	public JotrContext() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
		super();
		LogFunctions.log( "JotrContext.JotrContext()", "create JotrDH" );
		jotrDh = new JotrDH( JotrDH.DH1536_GENERATOR_S, JotrDH.DH1536_MODULUS_S, MIN_DH_KEY_LENGTH, "192.168.23.12" );
		LogFunctions.log( "JotrContext.JotrContext()", "create dsaKeyStorage" );
		dsaKeyStorage = new JotrDsaKeyStorage();
	}
	
	/**
	 * Returns the indicator for version 1 compatibility. (Not yet supported)
	 * 
	 * @return <b>true</b> if version 1 is enabled, <b>false</b> otherwise.
	 */
	public boolean isAllowV1() {
		return allowV1;
	}


	/**
	 * Sets the indicator for version 1 compatibility. (Not yet supported)
	 * 
	 * @param allowV1 Set <b>true</b> if version 1 is enabled, <b>false</b> otherwise.
	 */
	public void setAllowV1(boolean allowV1) {
		this.allowV1 = allowV1;
	}

	/**
	 * Returns the indicator for version 2 compatibility.
	 * 
	 * @return <b>true</b> if version 2 is enabled, <b>false</b> otherwise.
	 */
	public boolean isAllowV2() {
		return allowV2;
	}

	/**
	 * Sets the indicator for version 2 compatibility.
	 * 
	 * @param allowV2 Set <b>true</b> if version 2 is enabled, <b>false</b> otherwise.
	 */
	public void setAllowV2(boolean allowV2) {
		this.allowV2 = allowV2;
	}

	/**
	 * Returns the encryption required indicator.
	 * 
	 * @return <b>true</b> if encryption is required, <b>false</b> otherwise.
	 */
	public boolean isRequireEncryption() {
		return requireEncryption;
	}

	/**
	 * Sets the encryption required indicator.
	 * 
	 * @param requireEncryption Set <b>true</b> if encryption is required, <b>false</b> otherwise.
	 */
	public void setRequireEncryption(boolean requireEncryption) {
		this.requireEncryption = requireEncryption;
	}

	/**
	 * Returns the send whitespace-tag indicator.
	 * 
	 * @return <b>true</b> if whitespace will be send, <b>false</b> otherwise.
	 */
	public boolean isSendWhitespaceTag() {
		return sendWhitespaceTag;
	}

	/**
	 * Sets the send whitespace-tag indicator.
	 * 
	 * @param sendWhitespaceTag Set <b>true</b> if whitespace will be send, <b>false</b> otherwise.
	 */
	public void setSendWhitespaceTag(boolean sendWhitespaceTag) {
		this.sendWhitespaceTag = sendWhitespaceTag;
	}

	/**
	 * Returns the start ake on whitespace-tag indicator.
	 * 
	 * @return <b>true</b> if ake should be started on receiving whitespace-tag, <b>false</b> otherwise.
	 */
	public boolean isWhitespaceStartAke() {
		return whitespaceStartAke;
	}

	/**
	 * Sets the start ake on whitespace-tag indicator.
	 * 
	 * @param whitespaceStartAke Set <b>true</b> if ake should be started on receiving whitespace-tag, <b>false</b> otherwise.
	 */
	public void setWhitespaceStartAke(boolean whitespaceStartAke) {
		this.whitespaceStartAke = whitespaceStartAke;
	}

	/**
	 * Returns the start ake on error indicator.
	 * 
	 * @return <b>true</b> if ake should be started on receiving an error, <b>false</b> otherwise.
	 */
	public boolean isErrorStartAke() {
		return errorStartAke;
	}

	/**
	 * Sets the start ake on error indicator.
	 * 
	 * @param errorStartAke Set <b>true</b> if ake should be started on receiving an error, <b>false</b> otherwise.
	 */
	public void setErrorStartAke(boolean errorStartAke) {
		this.errorStartAke = errorStartAke;
	}

	/**
	 * Returns the current message state.
	 * 
	 * @return The message status.
	 * 
	 * @see #MSGSTATE_PLAINTEXT
	 * @see #MSGSTATE_ENCRYPTED
	 * @see #MSGSTATE_FINISHED
	 */
	public int getMessageState() {
		return messageState;
	}

	/**
	 * Sets the current message state.
	 * 
	 * @param messageState The message status to set.
	 * 
	 * @see #MSGSTATE_PLAINTEXT
	 * @see #MSGSTATE_ENCRYPTED
	 * @see #MSGSTATE_FINISHED
	 */
	protected void setMessageState( int messageState ) {
		this.messageState = messageState;
	}

	/**
	 * Returns the current authentication state.
	 * 
	 * @return The authentication status.
	 * 
	 * @see #AUTHSTATE_AWAITING_DHKEY
	 * @see #AUTHSTATE_AWAITING_REVEALSIG
	 * @see #AUTHSTATE_AWAITING_SIG
	 * @see #AUTHSTATE_NONE
	 */
	public int getAuthstate() {
		return authstate;
	}

	/**
	 * Sets the current authentication state.
	 * 
	 * @param authstate The authentication state to set.
	 * 
	 * @see #AUTHSTATE_AWAITING_DHKEY
	 * @see #AUTHSTATE_AWAITING_REVEALSIG
	 * @see #AUTHSTATE_AWAITING_SIG
	 * @see #AUTHSTATE_NONE
	 */
	protected void setAuthstate( int authstate ) {
		this.authstate = authstate;
	}

	/**
	 * Returns the Diffie-Hellman computation object.
	 * 
	 * @return The Diffie-Hellman computation object.
	 */
	public JotrDH getJotrDh() {
		return jotrDh;
	}

	/**
	 * Returns the AES key which is used to encrypt the public key in ake phase.
	 * 
	 * @return The AES key.
	 */
	public byte[] getAesKey() {
		return aesKey;
	}

	/**
	 * Sets the AES key which is used to encrypt the public key in ake phase.
	 * 
	 * @param aesKey The AES key to set.
	 */
	public void setAesKey( byte[] aesKey ) {
		this.aesKey = aesKey;
	}

	/**
	 * Returns the aes encrypted remote public key which is received in ake phase.
	 * 
	 * @return The aes encrypted remote public key which is received in ake phase.
	 */
	public byte[] getRemoteAesEncryptedPublicKey() {
		return remoteAesEncryptedPublicKey;
	}

	/**
	 * Sets the aes encrypted remote public key which is received in ake phase.
	 * 
	 * @param remoteAesEncryptedPublicKey
	 */
	public void setRemoteAesEncryptedPublicKey( byte[] remoteAesEncryptedPublicKey ) {
		this.remoteAesEncryptedPublicKey = remoteAesEncryptedPublicKey;
	}

	/**
	 * Returns the hash of the remote public key which is received in ake phase.
	 * 
	 * @return The hash of the remote public key which is received in ake phase.
	 */
	public byte[] getRemotePublicKeyHash() {
		return remotePublicKeyHash;
	}


	/**
	 * Sets  the hash of the remote public key which is received in ake phase.
	 * 
	 * @param remotePublicKeyHash The hash of the remote public key which is received in ake phase.
	 */
	public void setRemotePublicKeyHash( byte[] remotePublicKeyHash ) {
		this.remotePublicKeyHash = remotePublicKeyHash;
	}

	/**
	 * Returns the hash of the local public key which is received in ake phase.
	 * 
	 * @return The hash of the local public key which is received in ake phase.
	 */
	public byte[] getLocalPublicKeyHash() {
		return localePublicKeyHash;
	}

	/**
	 * Sets  the hash of the local public key which is received in ake phase.
	 * 
	 * @param localePublicKeyHash The hash of the local public key which is received in ake phase.
	 */
	public void setLocalPublicKeyHash( byte[] localePublicKeyHash ) {
		this.localePublicKeyHash = localePublicKeyHash;
	}

	/**
	 * Return the DSA key storage object.
	 * 
	 * @return The DSA key storage object.
	 */
	public JotrDsaKeyStorage getDsaKeyStorage() {
		return dsaKeyStorage;
	}

	/**
	 * Get current OTR protocol version. 
	 * 
	 * @return The OTR protocol version.
	 */
	public int getProtoVersion() {
		return version;
	}

	/**
	 * Sets the current OTR protocol version.
	 * 
	 * @param version The OTR protocol version.
	 */
	public void setProtoVersion(int version) {
		this.version = version;
	}
	
	/**
	 * Increments the local aes ctr counter value.
	 */
	public void incOurAesCounter() {
		ourAesCounter++;
	}
	
	/**
	 * Returns the local aes ctr counter value.
	 * 
	 * @return The local aes ctr counter value.
	 */
	public long getOurAesCounter() {
		return ourAesCounter;
	}

	/**
	 * Returns the remote aes ctr counter value.
	 * 
	 * @return The remote aes ctr counter value.
	 */
	public long getTheirAesCounter() {
		return theirAesCounter;
	}
	
	/**
	 * Sets the remote aes ctr counter value.
	 * 
	 * @param counter The remote aes ctr counter value to set.
	 */
	public void setTheirAesCounter( int counter ) {
		theirAesCounter = counter;
	}
}